/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.commons.core;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * Calculator.start(num1).add(num2).result()
 * <p>
 * 数学精准计算，除法结果默认采用四舍五入法保留小数 6 位。
 *
 * @author Mr.css
 * @version 2020-07-10 10:48
 * @see BigDecimal
 */
public class Calculator {

    public static Calculator start(Number num) {
        return new Calculator(num);
    }

    /**
     * @param num 初始数值
     * @throws IllegalArgumentException number is null
     */
    private Calculator(Number num) {
        Asserts.notNull(num, "the input number cannot be null");
        this.result = Converter.toBigDecimal(num);
    }

    /**
     * 数据结果
     */
    private BigDecimal result;
    /**
     * 小数点精度
     */
    private int scale = 6;
    /**
     * 除法默认采用四舍五入
     */
    private RoundingMode roundingMode = RoundingMode.HALF_UP;

    /**
     * 设置除法结果保留位数
     *
     * @param num 小数精度
     * @return -
     */
    public Calculator setScale(int num) {
        this.scale = num;
        return this;
    }

    /**
     * 设置除法结果的舍入模式，例如四舍五入法
     * <p>
     * ROUND_UP
     * ROUND_DOWN
     * ROUND_CEILING
     * ROUND_FLOOR
     * ROUND_HALF_UP
     * ROUND_HALF_DOWN
     * ROUND_HALF_EVEN
     * ROUND_UNNECESSARY
     *
     * @param mode rounding mode to apply.
     * @see BigDecimal
     */
    public Calculator setRoundingMode(RoundingMode mode) {
        this.roundingMode = mode;
        return this;
    }

    /**
     * 加
     *
     * @param num -
     * @return -
     * @throws IllegalArgumentException num is not number or num is null
     */
    public Calculator add(Object num) {
        Asserts.notNull(num, "the addendum cannot be null");
        result = result.add(Converter.toBigDecimal(num));
        return this;
    }

    /**
     * 除
     *
     * @param num -
     * @return -
     * @throws IllegalArgumentException num is not number or num is null
     */
    public Calculator divide(Object num) {
        return this.divide(num, this.scale, this.roundingMode);
    }

    /**
     * 除
     *
     * @param num   -
     * @param scale 小数精度
     * @return -
     * @throws IllegalArgumentException num is not number or num is null
     */
    public Calculator divide(Object num, int scale) {
        return this.divide(num, scale, this.roundingMode);
    }

    /**
     * 除
     *
     * @param num   -
     * @param scale 小数精度
     * @param mode  rounding mode to apply.
     * @return -
     * @throws IllegalArgumentException num is not number or num is null
     */
    public Calculator divide(Object num, int scale, RoundingMode mode) {
        Asserts.notNull(num, "the divisor cannot be null");
        BigDecimal divisor = Converter.toBigDecimal(num);
        Asserts.isTrue(!BigDecimal.ZERO.equals(divisor), "/ by zero");
        result = result.divide(divisor, scale, mode);
        return this;
    }

    /**
     * 乘
     *
     * @param num -
     * @return -
     * @throws IllegalArgumentException num is not number or num is null
     */
    public Calculator multiply(Object num) {
        Asserts.notNull(num, "the multiplier cannot be null");
        result = result.multiply(Converter.toBigDecimal(num));
        return this;
    }

    /**
     * 减
     *
     * @param num -
     * @return -
     * @throws IllegalArgumentException num is not number or num is null
     */
    public Calculator subtract(Object num) {
        Asserts.notNull(num, "the subtraction cannot be null");
        result = result.subtract(Converter.toBigDecimal(num));
        return this;
    }

    /**
     * 获取运算解雇
     *
     * @return BigDecimal
     */
    public BigDecimal result() {
        return result;
    }
}
